/**
 * 
 */
package qy.jalgotrade.barfeed.resampled;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import qy.jalgotrade.bar.Bar.Frequency;
import qy.jalgotrade.bar.Bars;
import qy.jalgotrade.barfeed.BaseBarFeed;
import qy.jalgotrade.event.EventHandler;
import qy.jalgotrade.utils.resamplebase.Grouper;
import qy.jalgotrade.utils.resamplebase.TimeRange;

/**
 * @author qy
 *
 */
public class ResampledBarFeed extends BaseBarFeed implements EventHandler {

	private List<Bars> __values;

	private BaseBarFeed __barFeed;

	private Grouper<Bars> __grouper;

	private TimeRange __range;

	/**
	 * 
	 * @param barFeed
	 * @param frequency
	 * @throws Exception
	 */
	public ResampledBarFeed(BaseBarFeed barFeed, Frequency frequency) throws Exception {

		this(barFeed, frequency, 0);
	}

	/**
	 * 
	 * @param barFeed
	 * @param frequency
	 * @param maxLen
	 * @throws Exception
	 */
	public ResampledBarFeed(BaseBarFeed barFeed, Frequency frequency, int maxLen) throws Exception {

		super(frequency, maxLen);

		// Register the same instruments as in the underlying barfeed.
		for (String instrument : barFeed.getRegisteredInstruments()) {
			registerInstrument(instrument);
		}

		__values = new ArrayList<>();
		__barFeed = barFeed;
		__grouper = null;
		__range = null;

		// 源 BarFeed 新产生 Bar(s) 可用时, 触发 self.__onNewValues(), 进行必要的 resample 处理工作:
		barFeed.getNewValuesReadyEvent().subscribe(this);
	}

	/*
	 * 
	 */
	@Override
	public void handle(String eventName, Map<String, Object> params) throws Exception {

		ZonedDateTime dateTime = (ZonedDateTime) params.get("dateTime");
		Bars values = (Bars) params.get("values");
		__onNewValues(dateTime, values);
	}

	/**
	 * 
	 * @param dateTime
	 * @param values
	 * @throws Exception
	 */
	private void __onNewValues(ZonedDateTime dateTime, Bars values) throws Exception {

		if (__range == null) {
			// 当前 时间周期 首个源 Bar(s):
			__range = TimeRange.buildRange(dateTime, getFrequency());
			__grouper = new BarsGrouper(__range.getBeginning(), values, getFrequency());
		} else if (__range.belongs(dateTime)) {
			// 当前 时间周期 的源 Bar(s):
			__grouper.addValue(values);
			// XXX: 是当前 TimeRange 最后一个 Bar, 与源 BarFeed 同步生成 ResampledBar:
			if (dateTime.plusSeconds(__barFeed.getFrequency().getValue()).compareTo(__range.getEnding()) >= 0) {
				__values.add(__grouper.getGrouped());
			}
		} else {
			// 开始下一个 时间周期:
			// XXX:
			/*if (!__values.contains(__grouper.getGrouped())) {
				__values.add(__grouper.getGrouped());
			}*/
			__range = TimeRange.buildRange(dateTime, getFrequency());
			__grouper = new BarsGrouper(__range.getBeginning(), values, getFrequency());
		}
	}

	/*
	 * 
	 */
	@Override
	public ZonedDateTime getCurrentDateTime() {

		return __barFeed.getCurrentDateTime();
	}

	/*
	 * 
	 */
	@Override
	public boolean barsHaveAdjClose() {

		return __barFeed.barsHaveAdjClose();
	}

	/*
	 * 
	 */
	@Override
	public Bars getNextBars() throws Exception {

		Bars ret = null;
		if (__values.size() > 0) {
			ret = __values.remove(__values.size() - 1);
		}
		return ret;
	}

	/*
	 * 
	 */
	@Override
	public void start() {

	}

	/*
	 * 
	 */
	@Override
	public void stop() {

	}

	/*
	 * 
	 */
	@Override
	public void join() {

	}

	/*
	 * 
	 */
	@Override
	public boolean eof() {

		return __barFeed.eof() && __values.size() == 0;
	}

	/*
	 * 
	 */
	@Override
	public ZonedDateTime peekDateTime() {

		/* 
		 * We can't determine when the next event will be generated since it'll
		 * depend on the values generated by the barfeed being wrapped.
		 */
		return null;
	}

	/**
	 * <pre>
	 * 确保当前 时间周期 结束后 (在接收到下个时间周期的第一个 Bar(s) 时), 生成当前 时间周期 Bar(s),
	 * 状态检查:  self.__range 非空且 dateTime 不在其范围内:
	 * </pre>
	 * 
	 * @param dateTime
	 * @throws Exception
	 */
	public void checkNow(ZonedDateTime dateTime) throws Exception {

		if (__range != null && !__range.belongs(dateTime)) {
			__values.add(__grouper.getGrouped());
			__grouper = null;
			__range = null;
		}
	}

	@Override
	public String toString() {

		return String.format("ResampledBarFeed {oriBarFeed: %s, freq: %s, currDatetime: %s}", __barFeed, getFrequency(),
		        getCurrentDateTime());
	}
}
